/**
 * Copyright 2010 Terrestrial Ecosystem Research Network, licensed under the Apache
 * License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or
 * agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and
 * limitations under the License.
 */

package au.org.tern.ecoinformatics.oai.provider;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;

import org.apache.log4j.Logger;

import ORG.oclc.oai.server.catalog.AbstractCatalog;
import ORG.oclc.oai.server.catalog.RecordFactory;
import ORG.oclc.oai.server.crosswalk.CrosswalkItem;
import ORG.oclc.oai.server.crosswalk.Crosswalks;
import ORG.oclc.oai.server.verb.BadArgumentException;
import ORG.oclc.oai.server.verb.BadResumptionTokenException;
import ORG.oclc.oai.server.verb.CannotDisseminateFormatException;
import ORG.oclc.oai.server.verb.IdDoesNotExistException;
import ORG.oclc.oai.server.verb.NoItemsMatchException;
import ORG.oclc.oai.server.verb.NoMetadataFormatsException;
import ORG.oclc.oai.server.verb.NoSetHierarchyException;
import ORG.oclc.oai.server.verb.OAIInternalServerError;
import au.org.tern.ecoinformatics.oai.provider.model.IdentifierRecord;
import au.org.tern.ecoinformatics.oai.provider.model.ListIdentifiersResponse;
import au.org.tern.ecoinformatics.oai.provider.model.Record;
import au.org.tern.ecoinformatics.oai.provider.service.OaiPmhService;
import au.org.tern.ecoinformatics.oai.provider.util.DateUtils;

public class BasicOaiCatalog extends AbstractCatalog {

	public static final String SET_COLLECTION = "collection";
	public static final String SET_DATASET =    "dataset";
	public static final String SET_PARTY =      "party";
	public static final String SET_ACTIVITY =   "activity";
	
	private static final Logger log = Logger.getLogger(BasicOaiCatalog.class);

	private Crosswalks crosswalks;
	
	private OaiPmhService oaiPmhService;
	
	@SuppressWarnings({"unchecked", "rawtypes"})
	public BasicOaiCatalog() {
		// Why? Ensures that any request for a metadataPrefix other than 'eml' gives appropriate 'unsupported format' error
		// We don't use the OAICat crosswalking, so in fact all we want is a non-null entry in the crosswalks map\
		// TODO: support other formats as needed
		Map crosswalkMap = new HashMap();
		crosswalkMap.put("eml", new CrosswalkItem("eml", "eml", "eml", "eml", 0));
		crosswalks = new Crosswalks(crosswalkMap);
	}
	
	public BasicOaiCatalog(Properties properties) {
		this();
	}
	
	public void setOaiPmhService(OaiPmhService oaiPmhService) {
		this.oaiPmhService = oaiPmhService;
	}

	@Override
	public void close() {
		// Nothing needs finalising here, we use Singleton SurveyDao from Spring applicationContext 
	}
	
	
	/**
	 * Return the appropriate record format for the given OAI identifier.
	 */
	@Override
	public String getRecord(String identifier, String metadataPrefix) throws IdDoesNotExistException, CannotDisseminateFormatException, OAIInternalServerError {
		Record record = oaiPmhService.getRecord(identifier, metadataPrefix);
	
		return "<record>" + RecordFactory.createHeader(record.identifier, record.datestamp, record.setSpec != null ? Arrays.asList(new String[] { record.setSpec })
						.iterator() : null, record.deleted)[0] + "<metadata>" + record.namespacedRecordXml + "</metadata></record>";
	}

	
	@Override
	@SuppressWarnings("rawtypes")
	public Vector getSchemaLocations(String identifier) throws IdDoesNotExistException,
			NoMetadataFormatsException, OAIInternalServerError {
		// TODO Auto-generated method stub
		return null;
	}

	
	@Override
	@SuppressWarnings("rawtypes")
	public Map listIdentifiers(String resumptionToken) throws BadResumptionTokenException,
			OAIInternalServerError {
		// We don't support resumption
		throw new BadResumptionTokenException();
	}
	

	@Override
	@SuppressWarnings({ "rawtypes", "unchecked", "static-access" })
	public Map listIdentifiers(
			String from, 
			String until, 
			String set,
            String metadataPrefix)
			throws BadArgumentException, CannotDisseminateFormatException, NoItemsMatchException,
			NoSetHierarchyException, OAIInternalServerError {
		
        // Lists to put results in
        List headers = new LinkedList();
        List identifiers = new LinkedList();
        Date fromDate = null;
        Date toDate = null;
        
        try {
        	fromDate = DateUtils.parseDate(from, DateUtils.OAI_DATE_FORMATS);
        	toDate = DateUtils.parseDate(until, DateUtils.OAI_DATE_FORMATS);
        }
        catch (ParseException pe) {
        	throw new BadArgumentException();
        }

        ListIdentifiersResponse records = oaiPmhService.listIdentifiers(fromDate, toDate, metadataPrefix, set);
        List setSpecs = new ArrayList();
        if (set != null) {
        	setSpecs.add(set);
        }
        if (records != null) {
	        if (records.getIdentifierRecords() != null) {
		        for (IdentifierRecord record : records.getIdentifierRecords()) {
		        	String[] header = getRecordFactory().createHeader(record.getIdentifier(), DateUtils.formatDate(record.getDate(), false), setSpecs.iterator(), record.isDeleted());
		        	headers.add(header[0]); 
		        	identifiers.add(header[1]); 
		        }
	        } 
	        else {
	        	throw new NoItemsMatchException();
	        }
        }
        else {
        	// Set not supported results in null records, throw BadArgumentException
        	throw new BadArgumentException(); 
        }
        // Put results in form needed to return
        Map results = new HashMap();
        results.put("headers", headers.iterator());
        results.put("identifiers", identifiers.iterator());

        return results;
	}


	@Override
	@SuppressWarnings("rawtypes")
	public Map listSets() throws NoSetHierarchyException, OAIInternalServerError {
		Map<String, Object> result = new HashMap<String, Object>();
		List<String> sets = new ArrayList<String>();
		for (String set : oaiPmhService.getSets()) {
			sets.add("<set><setSpec>" + set + "</setSpec><setName>" + set + "</setName></set>");
		}
		result.put("sets", sets.iterator());
		return result;
	}

	
	@Override
	@SuppressWarnings("rawtypes")
	public Map listSets(String resumptionToken) throws BadResumptionTokenException, OAIInternalServerError {
		// TODO Auto-generated method stub
		return null;
	}

	
	@Override
	public Crosswalks getCrosswalks() {
		return crosswalks;
	}

	
	public String toFinestFrom(String from) {
		return from;
	}

	
	public String toFinestUntil(String until) {
		return until; 
	}
}